package com.mall.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mall.common.config.JsonUtils;
import com.mall.common.domain.PageDTO;
import com.mall.common.exception.BizIllegalException;
import com.mall.common.utils.BeanUtils;
import com.mall.common.utils.ImageUploadUtils;
import com.mall.domain.dto.Item2DTO;
import com.mall.domain.dto.ItemDTO;
import com.mall.domain.dto.OrderDetailDTO;
import com.mall.domain.dto.SearchForm;
import com.mall.domain.po.Item;

import com.mall.domain.query.ItemPageQuery;
import com.mall.domain.response.Response;
import com.mall.mapper.ItemMapper;
import com.mall.service.ItemService;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
@Slf4j
public class ItemServiceImpl extends ServiceImpl<ItemMapper, Item> implements ItemService {

    private final ItemMapper itemMapper;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource(name = "taskExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Override
    public boolean selectById(Integer id) {
        Item item = itemMapper.selectById(id);
        return item != null;
    }

    @Override
    public List<ItemDTO> getItemByIds(Collection<Long> ids) {
        List<Item> items = itemMapper.selectBatchIds(ids);
        List<ItemDTO>itemDTOS = new ArrayList<>();
        for (Item item:items){
            ItemDTO itemDTO=new ItemDTO();
            BeanUtils.copyProperties(item,itemDTO);
            itemDTOS.add(itemDTO);
        }

        return itemDTOS;
    }

    @Override
    public Page<Item> pages(Integer pageNo,Integer pageSize,boolean isAsc,String sortBy) {
        Page<Item> itemPage = new Page<>(pageNo,pageSize);
        QueryWrapper<Item> itemQueryWrapper = new QueryWrapper<>();

        if (sortBy!=null&&!sortBy.isEmpty()){
            if(isAsc){
                itemQueryWrapper.orderByAsc(sortBy);
            }else itemQueryWrapper.orderByDesc(sortBy);
        }

        return page(itemPage,itemQueryWrapper);
    }

    @Override
    public Response<?> inserts(Item2DTO itemDTO) throws IOException {
        Item item = new Item();
        BeanUtils.copyProperties(itemDTO,item);
        if (itemMapper.selectById(item.getId()) != null){
            return Response.fail();
        }
        item.setCreateTime(LocalDateTime.now());
        item.setUpdateTime(LocalDateTime.now());
        // 图片上传
        String imageUrl = ImageUploadUtils.upload(itemDTO.getImage());
        item.setImage(imageUrl);

        itemMapper.insert(item);
        return Response.success();
    }

    @Override
    public boolean updateStatus(Integer id, Integer status) {
        Item item = itemMapper.selectById(id);
        if (item==null){
            return false;
        }
        item.setStatus(status);
        itemMapper.updateById(item);
        return true;
    }

    @Override
    public void updates(Item item) {
        item.setUpdateTime(LocalDateTime.now());
        itemMapper.updateById(item);
    }

    @Override
    public boolean delete(Long id) {
        if(itemMapper.selectById(id)==null){
        return false;
        }
        itemMapper.deleteById(id);
        return true;
    }

    /**
     * 批量扣减库存
     */
    @Override
    public void deDuckStock(List<OrderDetailDTO> items) {
        String sqlStatement = "com.mall.mapper.ItemMapper.updateStock";
        boolean r = false;
        try {
            r = executeBatch(items, (sqlSession, entity) -> sqlSession.update(sqlStatement, entity));
        } catch (Exception e) {
            throw new BizIllegalException("更新库存异常，可能是库存不足!", e);
        }
        if (!r) {
            throw new BizIllegalException("库存不足！");
        }
    }

    @Override
    public Response<?> categories() {
        List<String> categorieList = itemMapper.selectDistinctCategories();
        return Response.success(categorieList);
    }

    @Override
    public Response<?> brands() {
        List<String> brandList = itemMapper.selectDistinctbrands();
        return Response.success(brandList);
    }

    @Override
    public IPage<Item> getItemList(SearchForm searchForm, Integer page, Integer size) {
        Page<Item> itemPage = new Page<>(page,size);
        return itemMapper.getItemList(searchForm,itemPage);
    }

    @Override
    @Transactional
    public Response<?> batchStatus(List<Long> ids, Integer status) {
        // 1. 入参校验，避免无效操作
        if (ids == null || ids.isEmpty() || status == null) {
            return Response.fail("参数错误：ids不能为空且status必须指定");
        }

        // 2. 批量查询数据
        List<Item> items = itemMapper.selectBatchIds(ids);
        if (items.isEmpty()) {
            return Response.success("没有找到需要更新的数据");
        }

        // 3. 批量更新优化：使用批量更新方法替代循环单条更新
        // 3.1 先设置状态
        items.forEach(item -> item.setStatus(status));

        // 3.2 执行批量更新（假设itemMapper有批量更新方法）
        // 如果使用MyBatis-Plus，可直接使用updateBatchById
        boolean success = this.updateBatchById(items);

        // 4. 返回结果
        return success ? Response.success("批量更新成功") : Response.fail("批量更新失败");
    }

    @Override
    public Response<?> inserts(String name, Integer price, Integer stock, MultipartFile image, String category, String brand, String spec, Boolean isAD, Integer status) throws IOException {
        Item item = new Item();
        item.setName(name);
        item.setPrice(price);
        item.setStock(stock);
        item.setCategory(category);
        item.setBrand(brand);
        item.setSpec(spec);
        item.setIsAD(isAD);
        item.setStatus(status);

        LocalDateTime now = LocalDateTime.now();
        item.setCreateTime(now);
        item.setUpdateTime(now);

        // 图片上传
        String imageUrl = ImageUploadUtils.upload(image);
        item.setImage(imageUrl);

        itemMapper.insert(item);
        return Response.success();
    }

    /**
     * 加缓存
     */
    @Override
    public PageDTO search(ItemPageQuery query) {
        long startTime = System.currentTimeMillis();

        // 1. 生成缓存key（根据查询参数生成唯一标识）
        String cacheKey = "item:search:" +
                "key:" + (query.getKey() == null ? "" : query.getKey()) +
                ":brand:" + (query.getBrand() == null ? "" : query.getBrand()) +
                ":category:" + (query.getCategory() == null ? "" : query.getCategory()) +
                ":minPrice:" + (query.getMinPrice() == null ? "" : query.getMinPrice()) +
                ":maxPrice:" + (query.getMaxPrice() == null ? "" : query.getMaxPrice()) +
                ":page:" + query.getPageNo() +
                ":size:" + query.getPageSize();

        // 2. 尝试从Redis获取缓存
        String cachedResult = (String) redisTemplate.opsForValue().get(cacheKey);
        if (StringUtils.isNotBlank(cachedResult)) {
            // 反序列化JSON字符串为PageDTO对象
            PageDTO result = JsonUtils.parseObject(cachedResult, PageDTO.class);
            long endTime = System.currentTimeMillis();
            log.info("之后查询商品分页缓存所需耗时：{}ms", endTime - startTime);
            return result;
        }

        // 3. 缓存未命中，查询数据库
        Page<Item> res = this.lambdaQuery()
                .like(StrUtil.isNotBlank(query.getKey()), Item::getName, query.getKey())
                .eq(StrUtil.isNotBlank(query.getBrand()), Item::getBrand, query.getBrand())
                .eq(StrUtil.isNotBlank(query.getCategory()), Item::getCategory, query.getCategory())
                .eq(Item::getStatus, 1)
                .between(query.getMaxPrice() != null, Item::getPrice, query.getMinPrice(), query.getMaxPrice())
                .page(query.toMpPage("update_time", false));

        // 4. 封装结果
        PageDTO<ItemDTO> result = PageDTO.of(res, ItemDTO.class);

        // 5. 存入Redis缓存，设置过期时间（例如30分钟）
        // 异步将用户信息存入 Redis 缓存，提升响应速度
        threadPoolTaskExecutor.submit(() -> {
            long expireSeconds = 60 * 30 + RandomUtil.randomInt(60);
            String resultJson = JsonUtils.toJsonString(result);
            redisTemplate.opsForValue().set(cacheKey, resultJson, expireSeconds, TimeUnit.SECONDS);
        });
        long endTime = System.currentTimeMillis();
        log.info("第一次查询商品分页数据库所需耗时：{}ms", endTime - startTime);
        return result;
    }



}
